package butines.game.lastcolony.entities;

import static butines.game.lastcolony.entities.EntityAction.FLY;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;

import butines.core.GameImage;

public class Aircraft extends Entity {

    private static final EntityClass CHOPPER;
    private static final EntityClass WRAITH;
    
    static {
    
        CHOPPER = new EntityClass();
        CHOPPER.setName("Chopper");
        CHOPPER.setCost(900);
        CHOPPER.setPixelWidth(40);
        CHOPPER.setPixelHeight(40);
        CHOPPER.setPixelOffsetX(20);
        CHOPPER.setPixelOffsetY(20);
        CHOPPER.setWeaponType(WeaponType.HEAT_SEEKER);
        CHOPPER.setRadius(18);
        CHOPPER.setSight(6);
        CHOPPER.setCanAttack(true);
        CHOPPER.setCanAttackLand(true);
        CHOPPER.setCanAttackAir(true);
        CHOPPER.setHitPoints(50);
        CHOPPER.setSpeed(25);
        CHOPPER.setTurnSpeed(4);
        CHOPPER.setDirections(8);
        CHOPPER.setPixelShadowHeight(40);
        CHOPPER.setSpriteSheet(new GameImage("images/aircraft/chopper.png"));
        CHOPPER.addEntitySprite("fly", 4, 8);
        CHOPPER.setAction(FLY);
        CHOPPER.setSelectable(true);
        CHOPPER.setOrdersType(EntityOrders.Type.FLOAT);
        
        WRAITH = new EntityClass();
        WRAITH.setName("Wraith");
        WRAITH.setCost(600);
        WRAITH.setPixelWidth(30);
        WRAITH.setPixelHeight(30);
        WRAITH.setPixelOffsetX(15);
        WRAITH.setPixelOffsetY(15);
        WRAITH.setWeaponType(WeaponType.FIREBALL);
        WRAITH.setRadius(15);
        WRAITH.setSight(8);
        WRAITH.setCanAttack(true);
        WRAITH.setCanAttackLand(false);
        WRAITH.setCanAttackAir(true);
        WRAITH.setHitPoints(50);
        WRAITH.setSpeed(40);
        WRAITH.setTurnSpeed(4);
        WRAITH.setDirections(8);
        WRAITH.setPixelShadowHeight(40);
        WRAITH.setSpriteSheet(new GameImage("images/aircraft/wraith.png"));
        WRAITH.addEntitySprite("fly", 1, 8);
        WRAITH.setAction(FLY);
        WRAITH.setSelectable(true);
        WRAITH.setOrdersType(EntityOrders.Type.FLOAT);
        
    }
    
    public static Aircraft create(String name, Object ... values) {
        if ("chopper".equals(name)) {
            return CHOPPER.create(Aircraft.class, values);
        } else if ("wraith".equals(name)) {
            return WRAITH.create(Aircraft.class, values);
        }
        return null;
    }
    
    private int imageOffset;
    private int animationIndex;
    private EntitySprite imageList;
    
    public Aircraft() {
        
    }
    
    public void animate() {
        // Consider an item healthy if it has more than 40% life
        if (getLife() > getEntityClass().getHitPoints() * 0.4) {
            setLifeCode("healthy");
        } else if (getLife() <= 0) {
            setLifeCode("dead");
            getGame().remove(this);
            return;
        } else {
            setLifeCode("damaged");
        }
        
        if (getAction() == FLY) {
            float direction = wrapDirection(Math.round(this.direction), getDirections());
            imageList = getEntityClass().getEntitySprite("fly-" + (int) direction);
            imageOffset = imageList.getOffset() + animationIndex;
            animationIndex++;
            if (animationIndex >= imageList.getCount()) {
                animationIndex = 0;
            }
        }
    }
    
    private void drawLifeBar(Graphics2D g2d, float drawingX, float drawingY) {
        int x = (int) drawingX;
        int y = (int) (drawingY - 2 * game.getLifeBarHeight());

        Color color = isHealthy() ? game.getHealthBarHealthyFillColor() : game.getHealthBarDamagedFillColor();
        g2d.setColor(color);
        g2d.fillRect(x, y, (int) (getPixelWidth() * getLife() / (float) getHitPoints()), game.getLifeBarHeight());

        g2d.setColor(game.getHealthBarBorderColor());
        g2d.drawRect(x, y, getPixelWidth(), game.getLifeBarHeight());
    }
    
    private void drawSelection(Graphics2D g2d, float drawingX, float drawingY) {
        int x = (int) (drawingX + getPixelOffsetX());
        int y = (int) (drawingY + getPixelOffsetY());
        
        g2d.setColor(game.getSelectionFillColor());
        g2d.fillOval(x - getRadius(), y - getRadius(), getRadius() * 2, getRadius() * 2);

        Stroke stroke = g2d.getStroke();
        
        g2d.setStroke(new BasicStroke(2));
        g2d.setColor(game.getSelectionBorderColor());
        
        g2d.drawLine(x, y, x, y+getPixelShadowHeight());

        g2d.drawOval(x - getRadius(), y - getRadius(), getRadius() * 2, getRadius() * 2);

        g2d.drawOval(x - 4, y + getPixelShadowHeight() - 4, 8, 8);
     
        g2d.setStroke(stroke);
    }
    
    public void draw(Graphics2D g2d) {
        
        float sx = (getX() * getGame().getGridSize()) - getGame().getOffsetX() - getPixelOffsetX();
        float sy = (getY() * getGame().getGridSize()) - getGame().getOffsetY() - getPixelOffsetY() - getPixelShadowHeight();
        int colorIndex = (getTeam() == EntityTeam.BLUE) ? 0 : 1;
        int colorOffset = colorIndex * getEntityClass().getPixelHeight();
        int shadowOffset = getEntityClass().getPixelHeight() * 2; // The aircraft shadow is on the second row of
        
        int ix = imageOffset * getEntityClass().getPixelWidth();
        int iw = getEntityClass().getPixelWidth();
        int ih = getEntityClass().getPixelHeight();
        
        if (isSelected()) {
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            drawSelection(g2d, sx, sy);
            drawLifeBar(g2d, sx, sy);
        }
        
        getEntityClass().getSpriteSheet().draw(g2d, (int)sx, (int) sy, ix, colorOffset, iw, ih);

        getEntityClass().getSpriteSheet().draw(g2d, (int) sx, (int) sy + getPixelShadowHeight(), ix, shadowOffset, iw, ih);
        
    }
    
    private float lastMovementX;
    private float lastMovementY;
    
    public void processOrders() {
        lastMovementX = 0;
        lastMovementY = 0;
        switch (orders.getType()) {
            case MOVE:
                float x = orders.getX() - this.x;
                float y = orders.getY() - this.y;
                int r = getRadius() / getGame().getGridSize();
                // Move towards destination until distance from destination is less than aircraft radius
                float distanceFromDestinationSquared = (x * x + y * y);
                if (distanceFromDestinationSquared <= (r * r)) {
                    orders.setType(EntityOrders.Type.FLOAT);
                } else {
                    moveTo(orders.getX(), orders.getY());
                }
                break;
        }
    }
    
    

    // Make sure 0 <= direction < directions
    public float wrapDirection(float direction, int directions) {
        if (direction < 0) {
            direction += directions;
        }
        if (direction >= directions) {
            direction -= directions;
        }
        return direction;
    }

 
    // Finds the angle between two objects in terms of a direction (where 0 <= angle < directions)
    public float findAngle(float x, float y, Entity unit, int directions) {
        float dx = x - unit.x;
        float dy = y - unit.y;
        
        float direction  = (float) ((directions / 2) - (Math.atan2(dx, dy) * directions / (2 * Math.PI)));
        
        return wrapDirection(direction, directions);
    }
    
    // returns the smallest difference (value ranging between -directions/2 to +directions/2) between two angles (where 0 <= angle < directions)
    private float angleDiff(float angle1, float angle2, int directions) {
        if (angle1 >= directions / 2) {
            angle1 = angle1 - directions;
        }
        if (angle2 >= directions / 2) {
            angle2 = angle2 - directions;
        }
        float diff = angle2 - angle1;
        if (diff < -directions / 2) {
            diff += directions;
        }
        if (diff > directions / 2) {
            diff -= directions;
        }
        return diff;
    }

    private void moveTo(float x, float y) {
        // Find out where we need to turn to get to destination
        float newDirection = findAngle(x, y, this, getDirections());
        // Calculate difference between new direction and current direction
        float difference = angleDiff(direction, newDirection, getDirections());
        // Calculate amount that aircraft can turn per animation cycle
        float turnAmount = getTurnSpeed() * game.getTurnSpeedAdjustmentFactor();
        if (Math.abs(difference) > turnAmount) {
            direction = wrapDirection((direction + turnAmount * Math.abs(difference) / difference), getDirections());
        } else {
            // Calculate distance that aircraft can move per animation cycle
            float movement = getSpeed() * game.getSpeedAdjustmentFactor();
            // Calculate x and y components of the movement
            double angleRadians = -(direction / getDirections()) * 2 * Math.PI;
            lastMovementX = (float) -(movement * Math.sin(angleRadians));
            lastMovementY = (float) -(movement * Math.cos(angleRadians));
            this.x = (this.x + lastMovementX);
            this.y = (this.y + lastMovementY);
        }
    }
        
}
